package ro.flashbite.game_engine
{
	import flash.display.Sprite;
	
	import ro.flashbite.game_engine.consts.AppInfoConsts;
	import ro.flashbite.game_engine.consts.StatesGame;
	import ro.flashbite.game_engine.controller.LevelManager;
	import ro.flashbite.game_engine.controller.MenusManager;
	import ro.flashbite.game_engine.controller.SharedDataManager;
	import ro.flashbite.game_engine.data.AppDataStorage;
	import ro.flashbite.game_engine.data.PlayerData;
	import ro.flashbite.game_engine.data.intefaces.IAppDataStorage;
	import ro.flashbite.game_engine.interfaces.IGameEngine;
	import ro.flashbite.game_engine.proxy.LoadingProxy;
	import ro.flashbite.game_engine.view.LoadingScreen;
	import ro.flashbite.interfaces.IFunctionBindable;
	import ro.flashbite.interfaces.ISleepWakeupDestroy;
	import ro.flashbite.loaders.AssetManager;
	import ro.flashbite.loggers.ILogger;
	import ro.flashbite.loggers.LoggerFactory;
	import ro.flashbite.sound.SoundManager;
	
	internal final class GameEngine implements ISleepWakeupDestroy, IGameEngine
	{
		private static const APP_DATA_STORAGE_ID	:	String = "_!_appGlobalStorage_!_";
		
		private var _initModuleID		:	String;
		
		private var _soundManager		:	SoundManager;
		private var _assetManager		:	AssetManager;
		private var _sharedDataManager	:	SharedDataManager;
		private var _appDataStorage		:	AppDataStorage;
		private var _menusManager		:	MenusManager;
		private var _levelManager		:	LevelManager;
		private var _console			:	IFunctionBindable;
		
		private var _globalLoadingProxy	:	LoadingProxy; //this is used to load start assets
		private var _assetsXML			:	XML;
		
		private var _logger				:	ILogger;
		
		private var _appContainer		:	Sprite;
		private var _menusContainer		:	Sprite;
		private var _levelsContainer	:	Sprite;
		
		private var _loadingScreen		:	LoadingScreen;
		
		// ================================================================================================================================
		// CONSTRUCTOR
		// ================================================================================================================================
		
		public function GameEngine(appContainer:Sprite,
								   appID:String, 
								   loadingScreen:LoadingScreen,
								   assetsXML:XML, 
								   initModuleID:String,
								   menuList:Array, 
								   levelList:Array,
								   console:IFunctionBindable)
		{
			_constructor(appContainer, appID, loadingScreen, assetsXML, initModuleID, menuList, levelList, console);
		}
		private function _constructor(appContainer:Sprite,
									  appID:String, 
									  loadingScreen:LoadingScreen, 
									  assetsXML:XML, 
									  initModuleID:String,
									  menuList:Array, 
									  levelList:Array,
									  console:IFunctionBindable):void
		{
			_appContainer = appContainer;
			_loadingScreen = loadingScreen;
			_assetsXML = assetsXML;
			_initModuleID = initModuleID;
			_console = console;
			_globalLoadingProxy = new LoadingProxy(_loadingScreen);
			_logger = LoggerFactory.getLogger("GameEngine");
			
			_soundManager = new SoundManager(_assetsXML.soundTransform[0]);
			_assetManager = new AssetManager();
			
			//shared data
			_sharedDataManager = new SharedDataManager(appID);
			var dataStored:Object = _sharedDataManager.getData(APP_DATA_STORAGE_ID);
			_appDataStorage = (dataStored) ? AppDataStorage.unserialize(dataStored) : new AppDataStorage(1, new PlayerData("Player1"), new Vector.<PlayerData>());
			saveAppDataStorage();
			
			//levels are behind menus
			_levelsContainer = new Sprite();
			_appContainer.addChild(_levelsContainer);
			_levelManager = new LevelManager(_levelsContainer, _assetsXML, levelList, _loadingScreen, _assetManager, _soundManager, this);
			
			//menus are on top
			_menusContainer = new Sprite();
			_appContainer.addChild(_menusContainer);
			
			_menusManager = MenusManager.getInstance();
			_menusManager.initialize(_menusContainer, menuList, this);
		}
		
		// ================================================================================================================================
		// PUBLIC
		// ================================================================================================================================
		
		public function start():void
		{
			_appContainer.addChild(_loadingScreen);
			
			_logger.debug("Start loading init module");
			//start the loading of the init datas; on complete, show menu's ; for now, start the first level
			_globalLoadingProxy.loadAndRegisterAssets(_assetManager, 
													  _soundManager, 
													  _assetsXML.module.(@id == _initModuleID)[0], 
													  MainGame.INIT_MODULE_ID, 
													  onLoadingCompleteInitAssets);
		}
		
		// ================================================================================================================================
		// ISleepWakeupDestroy
		// ================================================================================================================================
		
		public function sleep():void
		{
			if (_assetManager) {
				_assetManager.sleep();
			}
			
			if (_soundManager) {
				_soundManager.sleep(); //sleep all sounds
			}
			
			if (_levelManager) {
				_levelManager.sleep();
				
				if (_levelManager.currentState == StatesGame.PAUSED) {
					if (_menusManager) {
						_menusManager.openPauseLevelMenu();
					}
				}
			}
			
			if (_menusManager) {
				_menusManager.sleep();
			}
		}
		
		public function wakeup():void
		{
			if (_assetManager) {
				_assetManager.wakeup();
			}
			
			if (_soundManager) {
				_soundManager.wakeup(_initModuleID); //resume only global sounds (from _initModuleID)
			}
			
			if (_menusManager) {
				_menusManager.wakeup();
			}
		}
		
		public function destroy():void
		{
			_loadingScreen = null;
			_assetsXML = null;
			_appContainer = null;
			
			if (_logger) {
				LoggerFactory.clearLogger(_logger.name);
				_logger = null;
			}
			_console = null;
			
			if (_appDataStorage) {
				//last save of storage data
				if (_sharedDataManager) saveAppDataStorage();
				
				_appDataStorage.destroy();
				_appDataStorage = null;
			}
			
			if (_sharedDataManager) {
				_sharedDataManager.destroy();
				_sharedDataManager = null;
			}
			
			_menusContainer = null;
			if (_menusManager) {
				_menusManager.destroy();
				_menusManager = null;
			}
			
			_levelsContainer = null;
			if (_levelManager) {
				_levelManager.destroy();
				_levelManager = null;
			}
			
			if (_globalLoadingProxy) {
				_globalLoadingProxy.destroy();
				_globalLoadingProxy = null;
			}
			
			if (_soundManager) {
				_soundManager.destroy();
				_soundManager = null;
			}
			
			if (_assetManager) {
				_assetManager.destroy();
				_assetManager = null;
			}
		}
		
		// ================================================================================================================================
		// IGameEngine
		// ================================================================================================================================
		
		//---------------------------------------------------------------------------------------------------------------------------------
		//asset manager getting data
		public function getLevelAssetContent(id:String, type:String):Object
		{
			var content:Object;
			try {
				content = _assetManager.getAsset(id, type, _levelManager.currentLevelID).getContent();
			} catch (e:Error) {}
			
			return content;
		}
		public function getGlobalAssetContent(id:String, type:String):Object
		{
			var content:Object;
			try {
				content = _assetManager.getAsset(id, type, MainGame.INIT_MODULE_ID).getContent();
			} catch (e:Error) {}
			
			return content;
		}
		//---------------------------------------------------------------------------------------------------------------------------------
		
		//---------------------------------------------------------------------------------------------------------------------------------
		//sound play/stop management
		public function playSound(ids:Array, closure:Function = null, loops:int = 1):Boolean
		{
			return _soundManager.play(ids, null, loops, true, closure);
		}
		public function playSoundNoOverwrite(ids:Array, closure:Function = null, loops:int = 1):Boolean
		{
			return _soundManager.play(ids, null, loops, false, closure);
		}
		public function stopSound(ids:Array):void
		{
			for each (var sndID:String in ids) {
				_soundManager.stopSoundByID(sndID);
			}
		}
		public function stopAllSounds():void
		{
			_soundManager.stopAllSounds();
		}
		public function getSoundLength(soundID:String):Number
		{
			var len:uint = SoundManager.getSoundLength(soundID);
			if (len != 0) len = len / 1000;
			
			return len; //return .89 secs instead of 890 milisecs
		}
		//---------------------------------------------------------------------------------------------------------------------------------
		
		//---------------------------------------------------------------------------------------------------------------------------------
		//global saving data
		public function saveSharedData(id:String, data:Object, overwrite:Boolean = true):void
		{
			_sharedDataManager.saveData(id, data, overwrite);
		}
		public function getSharedData(id:String):Object
		{
			return _sharedDataManager.getData(id);
		}
		//---------------------------------------------------------------------------------------------------------------------------------
		
		//---------------------------------------------------------------------------------------------------------------------------------
		//global application storage
		public function getAppDataStorage():IAppDataStorage
		{ 
			return _appDataStorage; 
		}
		public function saveAppDataStorage():void 
		{ 
			_sharedDataManager.saveData(APP_DATA_STORAGE_ID, AppDataStorage.serialize(_appDataStorage)); 
			
			//volume of application will be set here
			if (SoundManager.GlobalVolume != _appDataStorage.applicationVolume) {
				SoundManager.setGlobalVolume(_appDataStorage.applicationVolume);
			}
		}
		//---------------------------------------------------------------------------------------------------------------------------------
		
		//---------------------------------------------------------------------------------------------------------------------------------
		//console callbacks
		public function addConsoleBinding(bindingID:String, bindingFunction:Function, bindingParam:Array = null, overwriteSame:Boolean = true):void
		{
			if (_console) _console.addBinding(bindingID, bindingFunction, bindingParam, overwriteSame);
		}
		public function removeConsoleBinding(bindingID:String):void
		{
			if (_console) _console.removeBinding(bindingID);
		}
		//---------------------------------------------------------------------------------------------------------------------------------
		
		//---------------------------------------------------------------------------------------------------------------------------------
		//ILevelToEngine
		public function openPauseLevelMenu(id:String = null, data:* = null):void
		{
			//sleep level from _levelManager
			_levelManager.sleep();
			//open pause menu
			_menusManager.openPauseLevelMenu(id, data);
		}
		
		public function openCompleteLevelMenu(id:String = null, data:* = null):void
		{
			_levelManager.currentState = StatesGame.COMPLETED;
			_menusManager.openCompleteLevelMenu(id, data);
		}
		
		public function openCompleteGameMenu(id:String = null, data:* = null):void
		{
			_levelManager.currentState = StatesGame.COMPLETED;
			_menusManager.openCompleteGameMenu(id, data);
		}
		
		public function openGameOverLevelMenu(id:String = null, data:* = null):void
		{
			_levelManager.currentState = StatesGame.COMPLETED;
			_menusManager.openGameOverLevelMenu(id, data);
		}
		//---------------------------------------------------------------------------------------------------------------------------------
		
		//---------------------------------------------------------------------------------------------------------------------------------
		//IMenuToEngine
		public function loadLevel(id:String=null):void
		{
			//close the current menu
			_menusManager.closeCurrentMenu();
			//start the loading in manager
			var levelID:String = id;
			
			if (levelID == null) {
				_logger.debug("No level defined to load; Go read from sharedObject");
				levelID = getSharedData(AppInfoConsts.CURRENT_LEVEL_ID) as String;
			} else {
				saveSharedData(AppInfoConsts.CURRENT_LEVEL_ID, levelID);
			}
			//read character ID
			var characterID:String = getSharedData(AppInfoConsts.CURRENT_CHARACTER_ID) as String;
			
			_levelManager.loadLevel(levelID, characterID);
		}
		
		public function replayCurrentLevel():void
		{
			//close the current menu
			_menusManager.closeCurrentMenu();
			//replay current level
			_levelManager.replayLevel();
		}
		
		public function resumeCurrentLevel():void
		{
			//close the current menu
			_menusManager.closeCurrentMenu();
			//wakeup on current level
			_levelManager.wakeup();
		}
		
		public function closeCurrentLevel():void
		{
			//close the current menu
			_menusManager.closeCurrentMenu();
			//destroy current level
			_levelManager.unloadLevel(_levelManager.currentLevelID);
			//open main menu
			_menusManager.openMainMenu();
		}
		
		public function getCurrentLevelID():String { return _levelManager.currentLevelID; }
		//---------------------------------------------------------------------------------------------------------------------------------
		
		// ================================================================================================================================
		// PRIVATE
		// ================================================================================================================================
		
		private function onLoadingCompleteInitAssets():void
		{
			//remove loading screeen
			if (_loadingScreen.parent) {
				_loadingScreen.parent.removeChild(_loadingScreen);
			}
			_logger.debug("Init module load complete");
			
			//now we must start MenusManager
				//init
			_menusManager.initializeMenus();
				//open main menu
			_menusManager.openMainMenu();
		}
	}
}